﻿using MudEngine.Parsing;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace MudEngine.Runtime
{
    delegate void UpdateListsCallback(StackEventArgs args);
    delegate void UpdateProgramCallback(int pc);

    public partial class DebugForm : Form
    {
        private Interpreter interpreter;

        private Program lastProgram;

        public DebugForm()
        {
            InitializeComponent();

            this.FormClosing += DebugForm_FormClosing;
        }

        void DebugForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            this.interpreter.OnStackChanged -= interpreter_OnStackChanged;
            this.interpreter.OnInstructionChanged -= interpreter_OnInstructionChanged;
        }

        public DebugForm(Interpreter interpreter) : this()
        {
            // TODO: Complete member initialization
            this.interpreter = interpreter;

            this.interpreter.OnStackChanged += interpreter_OnStackChanged;
            this.interpreter.OnInstructionChanged += interpreter_OnInstructionChanged;

            this.interpreter.RunTo(-1);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        void interpreter_OnInstructionChanged(object sender, DebugEventArgs args)
        {
            UpdateProgramCallback update = new UpdateProgramCallback(UpdateProgramList);
            this.Invoke(update, args.ProgramCounter);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="args"></param>
        private void UpdateProgramList(int pc)
        {
            foreach(ListViewItem lvi in lvProgramCode.Items)
            {
                string pIdx = lvi.SubItems[0].Text;
                int idx = int.Parse(pIdx);
                if (idx == pc)
                {
                    lvi.BackColor = Color.Blue;
                    lvi.ForeColor = Color.White;
                }
                else if (idx == breakAt)
                {
                    lvi.BackColor = Color.Red;
                    lvi.ForeColor = Color.White;
                }
                else
                {
                    lvi.BackColor = SystemColors.Window;
                    lvi.ForeColor = SystemColors.WindowText;
                }
            }

        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        void interpreter_OnStackChanged(object sender, StackEventArgs args)
        {
            UpdateListsCallback update = new UpdateListsCallback(UpdateListItems);
            this.Invoke(update, args);
        }

        private void UpdateListItems(StackEventArgs args)
        {
            if (lastProgram != args.Program && args.Program != null)
            {
                lastProgram = args.Program;
                lblFilename.Text = lastProgram.Filename;

                lvProgramCode.Items.Clear();

                int progCounter = 1;
                string[] line = new string[5];

                while (progCounter < lastProgram.ProgramSize)
                {
                    OpCodes code = lastProgram.ReadOpCode(progCounter++);
                    line[0] = (progCounter - 1).ToString();
                    line[1] = Enum.GetName(typeof(OpCodes), code);
                    line[2] = string.Empty;
                    line[3] = string.Empty;
                    line[4] = string.Empty;

                    switch (code)
                    {
                        case OpCodes.ldc_i2:
                            line[2] = lastProgram.Load2(progCounter).ToString(); progCounter += 2; break;
                        case OpCodes.sloc:
                        case OpCodes.sglob:
                        case OpCodes.sarg:
                        case OpCodes.lstr:
                        case OpCodes.lloc:
                        case OpCodes.lglob:
                        case OpCodes.larg:
                        case OpCodes.jmp:
                        case OpCodes.jmpf:
                        case OpCodes.ldc_i4:
                            line[2] = lastProgram.Load4(progCounter).ToString(); progCounter += 4; break;
                        case OpCodes.ldc_i8:
                            line[2] = lastProgram.Load8(progCounter).ToString(); progCounter += 8; break;
                        case OpCodes.call:
                        case OpCodes.call_virt:
                            line[2] = lastProgram.Load2(progCounter).ToString(); progCounter += 2; line[3] = lastProgram.Load1(progCounter++).ToString(); break;
                        case OpCodes.call_i:
                            line[2] = lastProgram.Load1(progCounter++).ToString(); line[3] = lastProgram.Load2(progCounter).ToString(); progCounter += 2; line[4] = lastProgram.Load1(progCounter++).ToString(); break;
                        case OpCodes.ldc_i4_0:
                        case OpCodes.ldc_i4_1:
                        case OpCodes.ldc_i1_0:
                        case OpCodes.ldc_i1_1:
                        case OpCodes.ldc_i2_0:
                        case OpCodes.ldc_i2_1:
                        case OpCodes.add:
                        case OpCodes.sub:
                        case OpCodes.gt:
                        case OpCodes.ret:
                        case OpCodes.ceq:
                        case OpCodes.dup:
                        case OpCodes.pop:
                        case OpCodes.nop:
                            break;
                    }

                    lvProgramCode.Items.Add(new ListViewItem(line));
                }

            }

            try
            {
                lbStack.Items.Clear();
                for (int i = 0; i < args.Stack.Length; i++)
                {
                    lbStack.Items.Add(string.Format("[{0,3}] {1}", i, args.Stack[i]));
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
            }

        }

        private int breakAt = -1;

        private void btPause_Click(object sender, EventArgs e)
        {
            interpreter.Pause();
        }

        private void btContinue_Click(object sender, EventArgs e)
        {
            breakAt = -1;
            interpreter.Continue();
        }

        private void btRunTill_Click(object sender, EventArgs e)
        {
            interpreter.RunTo(breakAt);
        }

        private void btNext_Click(object sender, EventArgs e)
        {
            breakAt = -1;
            interpreter.Pause();
            interpreter.Continue();
        }

        private void lvProgramCode_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (lvProgramCode.SelectedItems.Count > 0)
            {
                string pIdx = lvProgramCode.SelectedItems[0].SubItems[0].Text;
                breakAt = int.Parse(pIdx);
            }
        }

    }
}
